package com.example.servlet.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class SysLogAspect {

    // 声明一个切点
    // execution 用于匹配方法执行连接点。在使用 Spring AOP 时，这是主要的点切点代号。
    @Pointcut("execution(public * *(..))")
    public void publicMethod() {}

    // within 将匹配限制在特定类型内的连接点（使用 Spring AOP 时，在匹配类型内声明的方法的执行）
    @Pointcut("within(com.example.servlet..*)")
    public void inTrading() {}

    // 您可以使用 &&、|| 和 !来组合快捷表达式。您还可以通过名称来引用点切分表达式。
    @Pointcut("publicMethod() && inTrading()")
    public void tradingOperation() {}

    // 将匹配限制在连接点的主题（在 Spring AOP 中运行的方法）具有给定注解的连接点。
    @Pointcut("@annotation(com.example.servlet.aop.SysLog)") // the pointcut expression
    private void annotation() {} // the pointcut signature

    // 在 @AspectJ 方面中使用 @Annotation 注解来声明切点表达式。
    @Before("@annotation(sysLog)")
    public void audit(SysLog sysLog) {
        String code = sysLog.value();
        // ...
    }

    // 通知与切点表达式相关联，并在切点匹配的方法执行之前、之后或周围（前后）运行
    @Before("execution(* com.example.servlet.*.*(..))")
    public void doBeforeCheck() {
        // ...
    }

    @AfterReturning("execution(* com.example.servlet.*.*(..))")
    public void doAfterReturningCheck() {
        // ...
    }

    @AfterThrowing("execution(* com.example.servlet.*.*(..))")
    public void doRecoveryActions() {
        // ...
    }

    @After("execution(* com.example.servlet.*.*(..))")
    public void doReleaseLock() {
        // ...
    }

    // 最后一种建议是环绕通知。环绕建议 “环绕 ”匹配方法的执行而运行。它有机会在方法运行之前和之后执行工作，并决定方法何时、如何甚至是否真正运行。
    // 如果需要以线程安全的方式共享方法执行前后的状态，例如启动和停止计时器，通常会使用周围建议。
    // 通过使用 @Around 注解对方法进行注解，可声明 Around 通知。方法应声明 Object 为其返回类型，方法的第一个参数必须是 ProceedingJoinPoint 类型。
    // 在通知方法的主体中，必须在 ProceedingJoinPoint 上调用 proceed() 才能运行底层方法。调用 proceed() 时，如果不带参数，调用者的原始参数将被提供给底层方法。
    @Around("execution(* com.example..service.*.*(..))")
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        try {
            return pjp.proceed();
        }
        catch(Exception ex) {
            throw ex;
        }
    }

}

// 参考官网 https://docs.springjava.cn/spring-framework/reference/core/aop.html
// 使用 Spring 进行面向方面编程 (AOP) 时，我们需要定义切面类，并使用 @Aspect 注解进行声明。
// 使用 @Aspect 注解声明一个切面类
// 要使用 Java @Configuration 启用 @AspectJ 支持，请添加 @EnableAspectJAutoProxy 注解、


// 首先，让我们定义一些 AOP 核心概念和术语。这些术语并非 Spring 专用。遗憾的是，AOP 术语并不特别直观。不过，如果 Spring 使用自己的术语，那就更令人困惑了。
// 1. 切面 (Aspect)
// 切面是关注点的一个抽象。它包含一个或多个通知，这些通知在特定事件发生时执行。
// 2. 通知 (Advice)
// 通知是切面的一部分。通知定义了在特定事件发生时应该执行的代码。通知可以包含以下类型：
//      前置通知 (Before Advice)
//      后置通知 (After Advice)
//      返回通知 (After Returning Advice)
//      异常通知 (After Throwing Advice)
//      环绕通知 (Around Advice)
// 3. 连接点 (Join Point)
// 连接点是指程序执行的某个特定位置。在 Spring AOP 中，连接点是指方法调用。
// 4. 切点 (Pointcut)
// 切点是指通知应用于哪些连接点的条件。切点可以是表达式或签名。
// 5. 引入 (Introduction)
// 引入是一种 AOP 术语，它允许我们在不修改现有类的情况下，引入新的接口或方法。
// 6. 织入 (Weaving)
// 7. 目标对象 (Target Object)
// 目标对象是被通知的对象。
// 9. 织入器 (Weaver)
// 10. 引介 (Introduction)

// Spring AOP 默认将标准 JDK 动态代理用于 AOP 代理。这样就可以代理任何接口（或接口集）。
// Spring AOP 还可以使用 CGLIB 代理。这是代理类而非接口所必需的。默认情况下，如果业务对象没有实现接口，就会使用 CGLIB。由于针对接口而非类编程是一种良好做法，
// 因此业务类通常会实现一个或多个业务接口。在需要向未在接口上声明的方法提供建议，或需要将代理对象作为具体类型传递给方法时（希望这种情况很少见），可以强制使用 CGLIB。